#!/usr/bin/python3
# -*- coding: utf-8 -*-

from configparser import ConfigParser
from hashlib import sha256
from json import loads
from os import path
from sys import exit
from time import sleep, time
from uuid import uuid1

from requests import post

from modules.api.base_translation import BaseTranslation
from modules.utils import MY_ERROR_CODES, get_file_encoding, print_err, print_warn

# 所有支持的语种字典
_YOUDAO_COMMON_LANGS = [
    'auto',
    'zh-CHS',
    'en',
    'ja',
    'ko',
    'fr',
    'es',
    'pt',
    'it',
    'ru',
    'vi',
    'de',
    'ar',
    'id',
    'af',
    'bs',
    'bg',
    'yue',
    'ca',
    'hr',
    'cs',
    'da',
    'nl',
    'et',
    'fj',
    'fi',
    'el',
    'ht',
    'he',
    'hi',
    'mww',
    'hu',
    'sw',
    'tlh',
    'lv',
    'ms',
    'mt',
    'no',
    'fa',
    'pl',
    'otq',
    'ro',
    'sr-Cyrl',
    'sr-Latn',
    'sk',
    'sl',
    'sv',
    'ty',
    'th',
    'to',
    'tr',
    'uk',
    'ur',
    'cy',
    'yua',
    'sq',
    'am',
    'hy',
    'az',
    'bn',
    'eu',
    'be',
    'ceb',
    'co',
    'eo',
    'tl',
    'fy',
    'gl',
    'ka',
    'gu',
    'ha',
    'haw',
    'is',
    'ig',
    'ga',
    'jw',
    'kn',
    'kk',
    'km',
    'ku',
    'ky',
    'lo',
    'la',
    'lb',
    'mk',
    'mg',
    'ml',
    'mi',
    'mr',
    'mn',
    'my',
    'ne',
    'ny',
    'ps',
    'pa',
    'sm',
    'gd',
    'st',
    'sn',
    'sd',
    'si',
    'so',
    'su',
    'tg',
    'ta',
    'te',
    'uz',
    'xh',
    'yi',
    'yo',
    'zu',
]

# 源语种字典
_YOUDAO_FROM_LANGS = {
    '1': {'n': '自动检测（推荐）', 'c': 'auto'},
    '2': {'n': '中文', 'c': 'zh-CHS'},
    '3': {'n': '粤语', 'c': 'yue'},
    '4': {'n': '英文', 'c': 'en'},
    '5': {'n': '日文', 'c': 'ja'},
    '6': {'n': '俄文', 'c': 'ru'},
    '7': {'n': '法文', 'c': 'fr'},
    '8': {'n': '韩文', 'c': 'ko'},
    '9': {'n': '西班牙文', 'c': 'es'},
    '10': {'n': '葡萄牙文', 'c': 'pt'},
    '11': {'n': '意大利文', 'c': 'it'},
    '12': {'n': '德文', 'c': 'de'},
    '13': {'n': '克罗地亚语', 'c': 'hr'},
    '14': {'n': '捷克语', 'c': 'cs'},
    '15': {'n': '丹麦语', 'c': 'da'},
    '16': {'n': '荷兰语', 'c': 'nl'},
    '17': {'n': '爱沙尼亚语', 'c': 'et'},
    '18': {'n': '芬兰语', 'c': 'fi'},
    '19': {'n': '希腊语', 'c': 'el'},
    '20': {'n': '波兰语', 'c': 'pl'},
}

# 常用目标语种字典
_YOUDAO_TO_LANGS = {
    '1': {'n': '中文', 'c': 'zh-CHS'},
    '2': {'n': '粤语', 'c': 'yue'},
    '3': {'n': '英文', 'c': 'en'},
    '4': {'n': '日文', 'c': 'ja'},
}

# 错误码表
_ERROR_CODES = {
    '101': '缺少必填的参数！首先确保必填参数齐全，然后确认参数书写是否正确。',
    '102': '不支持的语言类型！',
    '103': '翻译文本过长！',
    '104': '不支持的API类型！',
    '105': '不支持的签名类型！',
    '106': '不支持的响应类型！',
    '107': '不支持的传输加密类型！',
    '108': '应用ID无效！注册账号，登录后台创建应用和实例并完成绑定，可获得应用ID和应用密钥等信息。',
    '109': 'batchLog格式不正确！',
    '110': '无相关服务的有效实例！应用没有绑定服务实例，可以新建服务实例，绑定服务实例。注：某些服务的翻译结果发音需要tts实例，需要在控制台创建语音合成实例绑定应用后方能使用。',
    '111': '开发者账号无效！',
    '112': '请求服务无效！',
    '113': 'q不能为空！',
    '114': '不支持的图片传输方式！',
    '116': 'strict字段取值无效！请参考文档填写正确参数值。',
    '201': '解密失败！可能为DES,BASE64,URLDecode的错误。',
    '202': '签名检验失败！如果确认应用ID和应用密钥的正确性，仍返回202，一般是编码问题。请确保翻译文本 q 为UTF-8编码。',
    '203': '访问IP地址不在可访问IP列表！',
    '205': '请求的接口与应用的平台类型不一致！确保接入方式（Android SDK、IOS SDK、API）与创建的应用平台类型一致。如有疑问请参考入门指南。',
    '206': '因为时间戳无效导致签名校验失败！',
    '207': '重放请求！',
    '301': '辞典查询失败！',
    '302': '翻译查询失败！',
    '303': '服务端的其它异常！',
    '304': '会话闲置太久超时！',
    '401': '账户已经欠费！请进行账户充值。',
    '402': 'offlinesdk不可用！',
    '411': '访问频率受限！请稍后访问。',
    '412': '长请求过于频繁！请稍后访问。',
}


class YoudaoTranslation(BaseTranslation):
    '''
    有道智云API
    '''

    def __init__(self, section: str):

        BaseTranslation.__init__(self, section)
        self.section = section

        self._app_id = ''
        self._app_key = ''
        if self._conf and len(self._conf) > 0:
            self._app_id = self._conf['appId']
            self._app_key = self._conf['appKey']
        if self._app_id == '' or self._app_key == '':
            print_err('翻译API启用失败，请在api_keys.ini中配置appId和appKey！')
            exit(0)

        self._get_config()

    def translate(
        self, source_txt: str, to_lang: str, from_lang='auto', times=0
    ) -> str:
        '''
        开始翻译

        - source_txt: 源文本
        - to_lang: 目标语种
        - from_lang: 源语种
        - times: 重试次数
        '''

        # 当源语言不在受支持的语种范围内时，强制将源语言设置为auto，确保翻译能正常进行
        if not from_lang in _YOUDAO_COMMON_LANGS:
            from_lang = 'auto'
            print_warn('源语言不在受支持的语种范围内，已强制重置为“自动检测”！')

        # 当源语言不在受支持的语种范围内时，强制将源语言设置为zh-CHS，确保翻译能正常进行
        if not to_lang in _YOUDAO_COMMON_LANGS:
            to_lang = 'zh-CHS'
            print_warn('目标语言不在受支持的语种范围内，已强制重置为“中文”！')

        if to_lang == from_lang:
            print_warn('目标语种和源语种相同。')
            return source_txt

        # 原文本长度超过上限
        if len(source_txt) > self._max_char:
            print_err(MY_ERROR_CODES['ERR_10002'])
            return 'ERR_10002'

        # 重试次数大于3次依然未成功，则返回错误代码
        if times > 3:
            return 'ERR_10001'
        times += 1

        YOUDAO_URL = 'https://openapi.youdao.com/api'
        APP_KEY = self._app_id
        APP_SECRET = self._app_key

        def _encrypt(signStr: str):
            hash_algorithm = sha256()
            hash_algorithm.update(signStr.encode('utf-8'))
            return hash_algorithm.hexdigest()

        def _truncate(source: str):
            if source is None:
                return None
            size = len(source)
            return (
                source
                if size <= 20
                else source[0:10] + str(size) + source[size - 10 : size]
            )

        target = ''
        try:
            data = {}
            data['q'] = source_txt
            data['from'] = from_lang
            data['to'] = to_lang
            data['signType'] = 'v3'
            curtime = str(int(time()))
            data['curtime'] = curtime
            salt = str(uuid1())
            signStr = APP_KEY + _truncate(source_txt) + salt + curtime + APP_SECRET
            data['appKey'] = APP_KEY
            data['salt'] = salt
            data['sign'] = _encrypt(signStr)

            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
            response = post(YOUDAO_URL, data=data, headers=headers)

            result = loads(response.text)
            if 'translation' in result:
                target = loads(response.text)['translation'][0]

            elif 'errorCode' in result:
                _errcode = result['errorCode']
                if _errcode in ['304', '411', '412']:
                    print_warn(_ERROR_CODES[_errcode])
                    sleep(3)
                    return self.translate(source_txt, to_lang, from_lang, times)

                if _errcode in _ERROR_CODES:
                    print_err(_ERROR_CODES[_errcode])
                else:
                    print_err(f'翻译引擎出现未知错误，错误代码：{_errcode}！')
                target = 'ERR_10001'

            else:
                print_err('翻译引擎出现未知错误，请稍后重试！')
                target = 'ERR_10001'

        except Exception as e:
            print_err(f'翻译出现异常：{e.args[0]}！')
            target = 'ERR_10001'
        return target

    def _get_config(self):
        '''
        获取配置
        '''

        config_path = path.join(
            path.abspath('.'), 'modules', 'api', 'youdao', 'config.ini'
        )
        if not path.isfile(config_path):
            return

        conf = ConfigParser()  # 调用读取配置模块中的类
        conf.optionxform = lambda option: option  # 解决configparser将键值自动小写的问题
        conf.read(config_path, encoding=get_file_encoding(config_path))

        self._rate = conf.getint(self.section, 'rate')
        if self._rate < 1:
            self._rate = 1
        self._max_char = conf.getint(self.section, 'max_char')
        if self._max_char < 50:
            self._max_char = 2000

    def get_from_langs(self) -> dict:
        '''
        获取源语种字典
        '''
        return _YOUDAO_FROM_LANGS

    def get_to_langs(self) -> dict:
        '''
        获取语言字典
        '''
        return _YOUDAO_TO_LANGS
